gusucode.com > VC++ EMF图片浏览器(可读emf、wmf、emz、wmz、png……等)-源码程序 > VC++ EMF图片浏览器(可读emf、wmf、emz、wmz、png……等)-源码程序/code/Src/Client/scemflib/SCEMF.cpp

    //Download by http://www.NewXing.com
/*
*	This file is part of the EMFexplorer projet.
*	Copyright (C) 2004 Smith Charles.
*
*	This library is free software; you can redistribute it and/or
*	modify it under the terms of the GNU Lesser General Public
*	License as published by the Free Software Foundation; either
*	version 2.1 of the License, or (at your option) any later version.
*
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*
*	Extension: for commercial use, apply the Equity Public License, which
*	adds to the normal terms of the GLPL a condition of donation to the author.
*   If you are interested in support for this source code,
*   contact Smith Charles <smith.charles@free.fr> for more information.
*/


#include "stdafx.h"
#include "SCEMF.h"

#include "SCGenInclude.h"
#include SC_INC_WINLIB(SCBitmap.h)
#include SC_INC_WINLIB(SCMemDC.h)
#include SC_INC_WINLIB(SCRichEdit.h)
#include SC_INC_WINLIB(SCGDIUtils.h)
#include <wingdi.h>

//	#ifdef _DEBUG
//	#define new DEBUG_NEW
//	#undef THIS_FILE
//	static char THIS_FILE[] = __FILE__;
//	#endif

// 1 inch => 2540 0.01mm
#define L001MMPERINCH 2540L

//	#define SC_ADJUST_EMFDPI

#define SC_PRIVILEGE_EMFDPI

// Get DPI of EMF
BOOL SCGetEMFDPIs(HENHMETAFILE hEMF, long &lDPIX, long &lDPIY)
{
	long lEmfPaperCx;
	long lEmfPaperCy;
	return SCGetEMFInfos(hEMF, lDPIX, lDPIY, lEmfPaperCx, lEmfPaperCy);
}

// Get DPI and paper size of EMF
BOOL SCGetEMFInfos(HENHMETAFILE hEMF, long &lDPIX, long &lDPIY, long &lPaperCx, long &lPaperCy)
{
	ENHMETAHEADER EmfHeader;
	if (hEMF && ::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &EmfHeader))
	{
		// this paper size is not reliable (there are a lot of 1024x768 around there)
		lPaperCx = EmfHeader.szlDevice.cx;
		lPaperCy = EmfHeader.szlDevice.cy;

		// or this paper size (in mm) is not reliable (there are a lot of 320x240 around there,
		// yielding 81x81 DPI device)
		lDPIX = MulDiv(lPaperCx, 254, EmfHeader.szlMillimeters.cx*10L);
		lDPIY = MulDiv(lPaperCy, 254, EmfHeader.szlMillimeters.cy*10L);

#ifdef SC_ADJUST_EMFDPI
		// TODO: fix this or undef SC_ADJUST_EMFDPI
		// Ask Microsoft how to get 96 for EMF taylored on a 96 DPI device
		if (81==lDPIX)
			lDPIX = 96;
		if (81==lDPIY)
			lDPIY = 96;
#endif
		return TRUE;
	}
	
	return FALSE;
}


//------------------------------------
#define SC_KEEP_XY		0
#define SC_KEEP_Y		1
#define SC_KEEP_X		2
//	#define SC_USE_BOUNDS
///
/// Get size of the given EMF ( doesn't take page rotations into account).
/// (This function attempts to guess the best size to use in order to display an EMF
/// at actual size)
///
BOOL SCGetEMFPlaySize(HENHMETAFILE hEMF, CSize& sizeEMF, HDC hDC/*=NULL*/, float* pfScale/*=NULL*/)
{
	ASSERT(hEMF);

	// Collect information about the EMF
	ENHMETAHEADER EmfHeader;
	if (!::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &EmfHeader))
		return FALSE;

#ifdef SC_USE_BOUNDS
	sizeEMF.cx = EmfHeader.rclBounds.right - EmfHeader.rclBounds.left + 1;
	sizeEMF.cy = EmfHeader.rclBounds.bottom - EmfHeader.rclBounds.top + 1;
	if (sizeEMF.cx<=0)
		sizeEMF.cx = 1;
	if (sizeEMF.cy<=0)
		sizeEMF.cy = 1;
#else
	long lEmfDPIX = 0;
	long lEmfDPIY = 0;
	long lEmfPaperCx = 0;
	long lEmfPaperCy = 0;
	if (!SCGetEMFInfos(hEMF, lEmfDPIX, lEmfDPIY, lEmfPaperCx, lEmfPaperCy))
		return FALSE;

	int bKeep = SC_KEEP_XY;
	int iWidth, iHeight;

	{// Scale dimensions to ensure correct aspect ratio
		BOOL bTmpDC = (NULL==hDC);
		if (bTmpDC)
		{
			hDC = ::GetDC(NULL);
		}
		ASSERT(hDC);
		if (!hDC)
			return FALSE;

		// adjust paper dimensions for device play
		// (for DPI independence, in case EMF DPIs and DPIs are different)
		if (!SCScaleImgSurface(hDC, lEmfDPIX, lEmfDPIY, lEmfPaperCx, lEmfPaperCy))
		{
			ASSERT(0);
			return FALSE;
		}

		// compute which paper dimension to keep, while preserving aspect ratio
		// (always keep the smaller dimension)
#ifdef SC_PRIVILEGE_EMFDPI
		// Using supposed EMF DPI instead of device DPI
		iWidth = MulDiv(EmfHeader.rclFrame.right - EmfHeader.rclFrame.left + 1, lEmfDPIX, L001MMPERINCH);
		iHeight = MulDiv(EmfHeader.rclFrame.bottom - EmfHeader.rclFrame.top + 1, lEmfDPIY, L001MMPERINCH);
#else
		GetEMFDimension(hDC, EmfHeader, iWidth, iHeight);
#endif
		if (!SCScaleImgSurface(hDC, lEmfDPIX, lEmfDPIY, (long&)iWidth, (long&)iHeight))
		{
			ASSERT(0);
			return FALSE;
		}
		if (iWidth<=0)
			iWidth = 1;
		if (iHeight<=0)
			iHeight = 1;

		if ((iWidth>lEmfPaperCx) || (iHeight>lEmfPaperCy))
		{// EMF was taylored with different resolution
			if (lEmfPaperCx>=lEmfPaperCy)
			{// keep y, and reduce x
				bKeep = SC_KEEP_Y;
			} else
			{// keep x, and reduce y
				bKeep = SC_KEEP_X;
			}
		} else
		{
			lEmfPaperCx = iWidth;
			lEmfPaperCy = iHeight;
		}

		// done with DC
		if (bTmpDC)
			::ReleaseDC(NULL,hDC);
	}

	// Final dimensions
	if (SC_KEEP_XY==bKeep)
	{
		sizeEMF.cx = lEmfPaperCx;
		sizeEMF.cy = lEmfPaperCy;
		if (pfScale)
			*pfScale = 1;
	} else
	if (SC_KEEP_Y==bKeep)
	{
		sizeEMF.cy = min(lEmfPaperCy, iHeight);
		sizeEMF.cx = ::MulDiv(sizeEMF.cy, iWidth, iHeight);
		if (sizeEMF.cx<=0)
			sizeEMF.cx = 1;
		if (pfScale)
			*pfScale = float(sizeEMF.cx)/float(lEmfPaperCx);
	} else
	{
		sizeEMF.cx = min(lEmfPaperCx, iWidth);
		sizeEMF.cy = ::MulDiv(sizeEMF.cx, iHeight, iWidth);
		if (sizeEMF.cy<=0)
			sizeEMF.cy =1;
		if (pfScale)
			*pfScale = float(sizeEMF.cy)/float(lEmfPaperCy);
	}
	// Note: at this point sizeEMF may be equal to rclBounds, meaning it may exceed
	// paper size (most likely for thin, long, images that require floating points
	// calculation for proper scaling).
#endif
	return TRUE;
}

#undef SC_KEEP_XY
#undef SC_KEEP_Y
#undef SC_KEEP_X
//-------------------------------

///
/// Smallest rectangle surrounding the image.
///
BOOL SCGetEMFElemsRect(HENHMETAFILE hEMF, CRect& rcElems, fnSCPlayEnhMetaFile fnPlayEMF/*=NULL*/)
{
	ENHMETAHEADER EmfHeader;
	if (hEMF && ::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &EmfHeader))
	{
#if 0
	// the 81 DPI problem would cumulate
		// compute bounds in pixels
		HDC hDC = ::GetDC(NULL);
		ASSERT(hDC);
		if (!hDC)
			return FALSE;
		
		// compute actual EMF DPIs, based on the dimensions of its creation device
		long lEmfDPIX = MulDiv(EmfHeader.szlDevice.cx, 254, EmfHeader.szlMillimeters.cx*10L);
		long lEmfDPIY = MulDiv(EmfHeader.szlDevice.cy, 254, EmfHeader.szlMillimeters.cy*10L);

#ifdef SC_ADJUST_EMFDPI
		// see SCGetEMFInfos for documentation
		if (81==lEmfDPIX)
			lEmfDPIX = 96;
		if (81==lEmfDPIY)
			lEmfDPIY = 96;
		//
#endif
		
		// scale the surface for DPI independence
		long lDevDPIX = ::GetDeviceCaps(hDC, LOGPIXELSX);
		long lDevDPIY = ::GetDeviceCaps(hDC, LOGPIXELSY);
		::ReleaseDC(NULL,hDC);
		ASSERT(lDevDPIX);
		ASSERT(lDevDPIY);

		rcElems.left = MulDiv(EmfHeader.rclBounds.left, lDevDPIX, lEmfDPIX);
		rcElems.top = MulDiv(EmfHeader.rclBounds.top, lDevDPIY, lEmfDPIY);
		rcElems.right = MulDiv(EmfHeader.rclBounds.right, lDevDPIX, lEmfDPIX);
		rcElems.bottom = MulDiv(EmfHeader.rclBounds.bottom, lDevDPIY, lEmfDPIY);

		// Ckeck bounds
		int iWidth = rcElems.Width();
		int iHeight = rcElems.Height();
		if (iWidth && iHeight)
		{
			// Ok! This is my bug: I won't allow an EMF whose dimensions exceed its device
			// to display at actual size.
			if (iWidth<=EmfHeader.szlDevice.cx && iHeight<=EmfHeader.szlDevice.cy)
			{
				// We take whatever is given
				return TRUE;
			} // else big EMFs will be sized down
		}
#endif
		SCComputeEMFBlackBox(hEMF, rcElems, fnPlayEMF);
		return TRUE;
	}

	return FALSE;
}

///
///  Compute EMF frame dimension in pixels.
///
void GetEMFDimension(HDC hDC, ENHMETAHEADER &emfh, int & width, int & height)
{
	// Calculate the rect in which to draw the metafile based on the
	// intended size and the current output device resolution.
	// Remember that the intended size is given in 0.01 mm units, so
	// convert those to device units on the target device.

#ifdef SC_PRIVILEGE_EMFDPI
	// Compute actual EMF DPIs, based on the dimensions of its creation device,
	// and prefer these DPIs over output device DPI
	int iDpiX = MulDiv(emfh.szlDevice.cx, 254, emfh.szlMillimeters.cx*10L);
	int iDpiY = MulDiv(emfh.szlDevice.cy, 254, emfh.szlMillimeters.cy*10L);
#else
	// Get the characteristics of the output device.
	int iDpiX = GetDeviceCaps(hDC,  LOGPIXELSX);
	int iDpiY = GetDeviceCaps(hDC,  LOGPIXELSY);
#endif
	
	// Convert from 0.01 mm to pixels.
	RECTL& rRect = emfh.rclFrame;
	width = MulDiv(rRect.right - rRect.left, iDpiX, L001MMPERINCH);
	height = MulDiv(rRect.bottom - rRect.top, iDpiY, L001MMPERINCH);
}

// Scale an image surface according to a destination device resolution
BOOL SCScaleImgSurface(HDC hDC, long lImgDPIX, long lImgDPIY, long &lImgCx, long &lImgCy)
{
	ASSERT(hDC);
	if (!hDC)
		return FALSE;

	long lDevDPIX = ::GetDeviceCaps(hDC, LOGPIXELSX);
	long lDevDPIY = ::GetDeviceCaps(hDC, LOGPIXELSY);

	ASSERT(lDevDPIX);
	ASSERT(lDevDPIY);
	if (lDevDPIX && lDevDPIY)
	{
		lImgCx = MulDiv(lImgCx, lDevDPIX, lImgDPIX);
		lImgCy = MulDiv(lImgCy, lDevDPIY, lImgDPIY);
		return TRUE;
	}
	return FALSE;
}

//
// Compute the dimensions, in device units, of the smallest rectangle that can be drawn
// around the picture stored in the metafile.
// Note: this is the job of Windows. But rclBounds is not always accurate. So we are
// taking an actual snapshot of the black box on a memory DC compatible with the screen.
//
// Note: Beware that this function clips any white-on-white element laying on the EMF's
// boundaries. That's the reason why ElemsRect may be smaller than EmfHeader.rclBounds.
// Empty lines are good examples of white-on-white elements.
//
BOOL SCComputeEMFBlackBox(HENHMETAFILE hEmf, CRect& ElemsRect, fnSCPlayEnhMetaFile fnPlayEMF/*=NULL*/)
{
	// We try to create the emf handle
	ASSERT(hEmf);
	ENHMETAHEADER EmfHeader;
	if (!hEmf || !::GetEnhMetaFileHeader(hEmf, sizeof(ENHMETAHEADER), &EmfHeader))
		return FALSE;
#ifdef SC_USE_BOUNDS
	// If we could rely on rclBounds
	CopyRect(&ElemsRect, (LPCRECT)&EmfHeader.rclBounds);
#else
	// Collect information from the EMF
	CSize sizeEMF;
	SCGetEMFPlaySize(hEmf, sizeEMF);
	CRect rcPlay(0, 0, sizeEMF.cx, sizeEMF.cy);

	// Draw to extract bounds (use a large surface)
	ElemsRect.CopyRect((LPRECT)&rcPlay);

	CSCMemDC MemDC;
	MemDC.SCPrepareSurface(sizeEMF.cx, sizeEMF.cy, hEmf);
	//MemDC.PatBlt(0, 0, sizeEMF.cx, sizeEMF.cy, WHITENESS); // background is white

	// Play in rcPlay to conform to aspect ratio
	BOOL bOk;
	if (NULL==fnPlayEMF)
		bOk = ::PlayEnhMetaFile(MemDC.m_hDC, hEmf, (LPRECT)&rcPlay);
	else
		bOk = fnPlayEMF(MemDC.m_hDC, hEmf, (LPRECT)&rcPlay);

	// Extract bounding box (start out with the whole bitmap surface)
	// final ElemsRect should be smaller or equal to this rcPlay
	CRect& R = ElemsRect;
	
	PBITMAPINFOHEADER pbih;     // bitmap info-header
	HBITMAP hDib = MemDC.STGetDCDIB();
	ASSERT(hDib);
	
	// Retrieve the information describing the DIB.
	DIBSECTION ds;
	::GetObject(hDib, sizeof(DIBSECTION), &ds);
	pbih = (PBITMAPINFOHEADER)&ds.dsBmih;
	ASSERT(ds.dsBm.bmBits); // must be a DIB
	
	// Compute image size in pixels
	DWORD dwBitsSize = (pbih->biSizeImage!=0) ?
		pbih->biSizeImage : abs(pbih->biHeight)*(((pbih->biWidth*pbih->biBitCount + 31) & ~31) >> 3);

	BYTE *pBottom = SCExtractMemBoundingBox((LPBYTE)ds.dsBm.bmBits, &R, dwBitsSize,
							ds.dsBm.bmWidthBytes, (short)ds.dsBm.bmBitsPixel);
	ASSERT(pBottom);
	
	if (!::IsRectEmpty(&R))
	{
		// adjustment for bottom-up image
		if (pbih->biHeight>0)
		{
			long t = rcPlay.bottom - R.bottom - 1;	  // new top = old height - pos of bottom pixel
			long b = t + (R.bottom - R.top);      // new top + new height
			// security
			ASSERT(t>=-1);
			if (t<0)
				t = 0;
			
			ASSERT(b>=-1);
			if (b<0)
				b = 0;
			
			// adjust rect
			R.top = t;		// number of blank pixels at the top
			R.bottom = b+1;	// below the bottom pixel
			R.right++;		// at right of the rightmost pixel
			// R.left = number of blank pixels on the left
		} else
		{ // adjust rect, for pixels on the left column and at the bottom line
			R.bottom++;
			R.right++;
		}
		
		ASSERT(R.left>=0);
		ASSERT(R.top>=0);
		ASSERT(R.right<=rcPlay.right);
		ASSERT(R.bottom<=rcPlay.bottom);
	}
#endif
	return TRUE;
}

///
/// Metafile to Enhanced Metafile format
///
HENHMETAFILE SCConvertWMFtoEMF(LPCTSTR lpszFname)
{
	// Open the file for reading. 
	CFile sourceFile;
	CFileException ex;
	if (!sourceFile.Open(lpszFname, CFile::modeRead, &ex))
		return NULL;

	UINT uiSize = sourceFile.GetLength();
	LPBYTE pBuffer = new BYTE[uiSize];
	if (!pBuffer)
	{
		sourceFile.Close();
		return NULL;
	}
	DWORD dwNbRead = sourceFile.Read(pBuffer, uiSize);
	sourceFile.Close();

	HENHMETAFILE hEMF = SCConvertWMFtoEMF(dwNbRead, pBuffer);

	// Cleanup objects.
	delete [] pBuffer;
	return hEMF;
}

HENHMETAFILE SCConvertWMFtoEMF(DWORD dwSize, LPBYTE pBuffer)
{
	LPBYTE pData = pBuffer;
	UINT uiSize = dwSize;
    APMFILEHEADER*	pAPMHeader = (APMFILEHEADER*)pBuffer;
	APMFILEHEADER	winWMFHeader;

	if (WMFMETA_PLACEABLEKEY==pAPMHeader->key)
	{// aldus
		pData += sizeof(APMFILEHEADER);
		uiSize -= sizeof(APMFILEHEADER);
	} else
	{// windows
		// Oh! You didn't place your metafile? Good for you!
		pAPMHeader = &winWMFHeader;
		memset(pAPMHeader, 0, sizeof(winWMFHeader));
	}
	HWND	ghwndDrawSurf = NULL;
	HDC hDCDrawSurf = GetDC(ghwndDrawSurf);
	
	METAFILEPICT mfPict;
	mfPict.hMF = 0;
	mfPict.mm = MM_ANISOTROPIC;

	METAFILEPICT *lpmfp   = NULL;
	if (pAPMHeader->inch)
	{
		lpmfp   = &mfPict;
		SMALL_RECT* prcPict = &pAPMHeader->bbox;
#if 1 
		mfPict.xExt = MulDiv((long)(prcPict->Right - prcPict->Left), 2540, pAPMHeader->inch);
		mfPict.yExt = MulDiv((long)(prcPict->Bottom - prcPict->Top), 2540, pAPMHeader->inch);
#else
		mfPict.xExt = 0;
		mfPict.yExt = 0;
		// Code from Microsoft's mfedit.c ==> BAD RESULT
		switch (pAPMHeader->inch) { 
			// !!! End up in an upside down image 
			// 
		case 1440: 
			mfPict.mm = MM_TWIPS; 
			break; 
		case 2540: 
			mfPict.mm = MM_HIMETRIC; 
			break; 
		case 254: 
			mfPict.mm = MM_LOMETRIC; 
			break; 
		case 1000: 
			mfPict.mm = MM_HIENGLISH; 
			break; 
		case 100: 
			mfPict.mm = MM_LOENGLISH; 
			break; 
		default: 
			// !!! In addition, text is too small 
			// 
			mfPict.mm = MM_ANISOTROPIC; 
			mfPict.xExt = ((long)(prcPict->Right - prcPict->Left)) * pAPMHeader->inch * 2560; 
			mfPict.yExt = ((long)(prcPict->Bottom - prcPict->Top)) * pAPMHeader->inch * 2560; 
			break; 
		}
#endif
	}
	
	// Generate EMF
	HENHMETAFILE hEMF = SetWinMetaFileBits(uiSize, pData, hDCDrawSurf, lpmfp);

	// Cleanup
	ReleaseDC(ghwndDrawSurf ,hDCDrawSurf);
	return hEMF;
}

///
/// Enhanced Metafile to Windows Metafile format on disk
///
void SCConvertEMFtoWMF(HENHMETAFILE hEMF, LPCTSTR lpszFileName)
{
	LPBYTE       lpEMFBits;
	UINT         uiSizeBuf;
	HDC			 hrefDC = GetDC(NULL);
	
	// Size of the Windows metafile associated with hMF.
	uiSizeBuf = GetWinMetaFileBits(hEMF, 0, NULL, MM_ANISOTROPIC, hrefDC);

	// Memory to hold metafile bits.
	lpEMFBits = new BYTE[uiSizeBuf];
	if (!lpEMFBits)
		return;

	// Bits of the enhanced metafile associated with hEMF.
	GetWinMetaFileBits(hEMF, uiSizeBuf, lpEMFBits, MM_ANISOTROPIC, hrefDC);

	// Copy the bits into a memory-based Windows metafile.
	HMETAFILE    hWMF = SetMetaFileBitsEx(uiSizeBuf, lpEMFBits);

	// Copy the Windows metafile to a disk-based Windows metafile.
	CopyMetaFile(hWMF, lpszFileName);

	// Clean up
	DeleteMetaFile(hWMF);

	delete [] lpEMFBits;
	ReleaseDC(NULL, hrefDC);
}

///
/// Copy the content of the source
///
HMETAFILE SCCopyEMFtoWMF(HENHMETAFILE hEMF, CRect rcSrc, METAFILEPICT *pMtf/*=NULL*/)
{

	CMetaFileDC	 MetaDC;
	HDC			 hAttribDC;
	BOOL		 bWholeEMF = (0==rcSrc.Width() || 0==rcSrc.Height());

	long lEmfDPIX = 0;
	long lEmfDPIY = 0;
	{
		long lEmfPaperCx = 0;
		long lEmfPaperCy = 0;
		if (!SCGetEMFInfos(hEMF, lEmfDPIX, lEmfDPIY, lEmfPaperCx, lEmfPaperCy))
			return NULL;
	}

	CSize sizeEMF;
	SCGetEMFPlaySize(hEMF, sizeEMF);
	CRect rcPlay(0, 0, sizeEMF.cx, sizeEMF.cy);

	// Draw in the metafile.
	MetaDC.Create(NULL);
	MetaDC.SetAttribDC(hAttribDC=CreateCompatibleDC(NULL)); // Required for some query functions working well.

	rcSrc.right++;
	rcSrc.bottom++;

	//MetaDC.SetMapMode(MM_ANISOTROPIC);
	MetaDC.SetWindowOrg(rcSrc.left, rcSrc.top);
	MetaDC.SetWindowExt(rcSrc.Width(), rcSrc.Height());

	if (!bWholeEMF)
	{
		CRgn rgn;
		rgn.CreateRectRgn(rcSrc.left, rcSrc.top, rcSrc.right, rcSrc.bottom);
		MetaDC.SelectClipRgn(&rgn, RGN_COPY);

	}
	MetaDC.PlayMetaFile(hEMF, (LPRECT)&rcPlay);

	HMETAFILE hMeta = MetaDC.Close();
	DeleteDC(hAttribDC);
	
	CPoint pt(rcSrc.Width(), rcSrc.Height());
	{
		HDC hDC = GetDC(NULL);
		int iMapMode = SetMapMode(hDC, MM_HIMETRIC);
		DPtoLP(hDC, &pt, 1);
		SetMapMode(hDC, iMapMode);
		ReleaseDC(NULL, hDC);
	}
	if (hMeta && pMtf)
	{// fill the placement header

		pMtf->hMF = hMeta;
		pMtf->mm = MM_ANISOTROPIC;
		pMtf->xExt = pt.x;
		pMtf->yExt = pt.y;
	}
	APMFILEHEADER apmheader;
	apmheader.key = WMFMETA_PLACEABLEKEY;
	apmheader.hmf = 0; // unused
	apmheader.bbox.Left = apmheader.bbox.Top = 0;
	apmheader.bbox.Right = pt.x;
	apmheader.bbox.Bottom = pt.y;
	apmheader.inch = 1440;
	apmheader.reserved = 0;
	apmheader.checksum = 0;
	{// compute checksum
		for (WORD *p = (WORD*)&apmheader; (p < (WORD*)&(apmheader.checksum)); ++p)
			apmheader.checksum ^= *p;
	} 

	return hMeta;
}

///
/// Close metafile DC and ensure that we never use it again
///
HENHMETAFILE SCCloseEMF(HDC& hEMFDC)
{
	HENHMETAFILE	hEmfTemp =	::CloseEnhMetaFile(hEMFDC);	
	hEMFDC = NULL;
	return hEmfTemp;
}

void SCRectDPtoHIMETRIC(CDC* pDC, RECT* pRect)
{
	ASSERT(pRect);
	pDC->DPtoHIMETRIC((LPSIZE)&pRect->left);
	pDC->DPtoHIMETRIC((LPSIZE)&pRect->right);
}


void SCRectLPtoHIMETRIC(CDC* pDC, RECT* pRect)
{
	ASSERT(pRect);
	pDC->LPtoHIMETRIC((LPSIZE)&pRect->left);
	pDC->LPtoHIMETRIC((LPSIZE)&pRect->right);
}

void SCRectToTwips(RECT* pRect, int ilogx, int ilogy, CDC* pDC)
{
	ASSERT(pRect);
	// convert to inches and inches to twips: 1440 twips = 1 inch
	pRect->left = MulDiv(pRect->left, 1440, ilogx);
	pRect->right = MulDiv(pRect->right, 1440, ilogx);
	pRect->top = MulDiv(pRect->top, 1440, ilogy);
	pRect->bottom = MulDiv(pRect->bottom, 1440, ilogy);
}

// Default paper (A4, in mm)
#define SC_DFLT_PAPER_CX		210
#define SC_DFLT_PAPER_CY		297
#define SC_DFLT_DPI				96
#define SC_DFLT_PHYSICALWIDTH	MulDiv(SC_DFLT_PAPER_CX*10, SC_DFLT_DPI, 254)
#define SC_DFLT_PHYSICALHEIGHT	MulDiv(SC_DFLT_PAPER_CY*10, SC_DFLT_DPI, 254)
	

UINT SCRichEditConvertToEMF(CRichEditCtrl& rRichEdit, HEMFVECTOR& rVector)
{
	// Setup paper
	HDC hdc;
	int nHorizRes;
	int nVertRes;
	int nMarginX = MulDiv(250, 1440, 254); // twips (2.5cm) 
	int nMarginY = nMarginX;			   // all around

	// Check default printer
	BOOL bPrnDC = FALSE;
#if 1
	if ((hdc = SCGetDefaultPrinterDC(rRichEdit.m_hWnd))!=NULL)
#else
	if (FALSE)
	// test stuff: to force using screen attributes
#endif
	{
		bPrnDC = TRUE;

		nHorizRes = GetDeviceCaps(hdc, PHYSICALWIDTH);
		nVertRes = GetDeviceCaps(hdc, PHYSICALHEIGHT);

		SetMapMode(hdc, MM_TEXT);
	} else
	{
		hdc = GetDC(NULL);

		// Note: may cause problem, since we are not using GetDeviceCaps as we should.
		// See the adjustements below.

		// Compute pixels by hand for an A4 paper at 96 dpi.
		nHorizRes = SC_DFLT_PHYSICALWIDTH;
		nVertRes = SC_DFLT_PHYSICALHEIGHT;
	}

	CDC* pDC = CDC::FromHandle(hdc);
	int nLogPixelsX = pDC->GetDeviceCaps(LOGPIXELSX);
	int nLogPixelsY = pDC->GetDeviceCaps(LOGPIXELSY);
	
	if (!bPrnDC)
	{// Adjustement for actual DPI
		nHorizRes = MulDiv(nHorizRes, nLogPixelsX, SC_DFLT_DPI);
		nVertRes = MulDiv(nVertRes, nLogPixelsY, SC_DFLT_DPI);
	}

	CRect rcMeta(0, 0, nHorizRes, nVertRes);
	SCRectLPtoHIMETRIC(pDC, &rcMeta);  // in 100th of mm
	
	FORMATRANGE fr;
	fr.hdcTarget = hdc;
	
	::SetRect(&fr.rcPage, 0, 0, nHorizRes, nVertRes);
	SCRectToTwips(&fr.rcPage, nLogPixelsX, nLogPixelsY, pDC);

	::CopyRect(&fr.rc, &fr.rcPage);
	::InflateRect(&fr.rc, -nMarginX, -nMarginY);

	long lTextLen = rRichEdit.GetTextLength();
	long lTextOut = 0; 
	
	fr.chrg.cpMax = -1;
	while (lTextOut<lTextLen)
	{
		CMetaFileDC MetaDC;
		BOOL bOK = MetaDC.CreateEnhanced(pDC, NULL, &rcMeta, NULL);
		ASSERT(bOK);
		
		fr.hdc = MetaDC.m_hDC;
		fr.chrg.cpMin = lTextOut;
		lTextOut = rRichEdit.FormatRange(&fr, TRUE);

		HENHMETAFILE hemf = MetaDC.CloseEnhanced();
		if (!bPrnDC && hemf)
		{
			// Device adjustment.
			// We must update the header to reflect the device we want
			// (the one we computed by hand)
			UINT uiSize = GetEnhMetaFileBits(hemf, 0, NULL);
			LPBYTE pData = uiSize ? new BYTE[uiSize] : NULL;
			if (pData)
			{
				GetEnhMetaFileBits(hemf, uiSize, pData);
				
				ENHMETAHEADER* pEmfHeader = (ENHMETAHEADER*)pData;
				pEmfHeader->szlDevice.cx = nHorizRes;
				pEmfHeader->szlDevice.cy = nVertRes;
				pEmfHeader->szlMillimeters.cx = SC_DFLT_PAPER_CX;
				pEmfHeader->szlMillimeters.cy = SC_DFLT_PAPER_CY;

				DeleteEnhMetaFile(hemf);
				hemf = SetEnhMetaFileBits(uiSize, pData);
				delete [] pData;
			} // else stay with bogus emf
		}
		ASSERT(hemf);
		rVector.push_back(hemf);
	}
	
	// tell the control to release cached information
	rRichEdit.FormatRange(NULL, FALSE);

	if (bPrnDC)
		DeleteDC(hdc);
	else
		ReleaseDC(NULL, hdc);

	return rVector.size();
}

///
/// RTF or TXT (non UNICODE) file to Enhanced Metafile format
///
UINT SCConvertRTFtoEMF(LPCTSTR lpszFname, HEMFVECTOR& rVector, BOOL bRTF/*=TRUE*/)
{
	CSCRichEdit RichEdit;
	if (!RichEdit.SCCreateHidden(bRTF))
		return 0;
	if (!RichEdit.SCLoadFromFile(lpszFname, bRTF))
		return 0;

	return SCRichEditConvertToEMF(RichEdit, rVector);
}


///
/// RTF or TXT (UNICODE) piece of memory to Enhanced Metafile format
///
UINT SCConvertRTFtoEMF(HANDLE hMem, HEMFVECTOR& rVector, BOOL bRTF/*=TRUE*/)
{
	CSCRichEdit RichEdit;
	if (!RichEdit.SCCreateHidden(bRTF))
		return 0;
	if (!RichEdit.SCLoadFromHGlobal(hMem, bRTF))
		return 0;

	return SCRichEditConvertToEMF(RichEdit, rVector);
}

///
/// Attempt to paste the clipboard content to Enhanced Metafile
///
UINT SCConvertClipboardTextToEMF(HEMFVECTOR& rVector)
{
	CSCRichEdit RichEdit;
	if (!RichEdit.SCCreateHidden(FALSE))
		return 0;

	if (!RichEdit.CanPaste())
		return 0;

	RichEdit.Paste();
	return SCRichEditConvertToEMF(RichEdit, rVector);
}

#undef SC_DFLT_PAPER_CX
#undef SC_DFLT_PAPER_CY
#undef SC_DFLT_DPI
#undef SC_DFLT_PHYSICALWIDTH
#undef SC_DFLT_PHYSICALHEIGHT

///
/// Adjust an EMF header to circumvent problems raised by the use of screen DCs
/// as reference DC when creating/translating/filtering an EMF
///
BOOL SCUpdateEMFheader(HENHMETAFILE& hemf, ENHMETAHEADER& rEmfHeader)
{
	ASSERT(hemf);
	if (0)
	{
		// Device adjustment.
		// We must update the header to reflect the device we want
		// (the one we computed by hand)
		UINT uiSize = GetEnhMetaFileBits(hemf, 0, NULL);
		LPBYTE pData = uiSize ? new BYTE[uiSize] : NULL;
		if (pData)
		{
			GetEnhMetaFileBits(hemf, uiSize, pData);
			
			ENHMETAHEADER* pEmfHeader = (ENHMETAHEADER*)pData;
			pEmfHeader->szlDevice.cx = rEmfHeader.szlDevice.cx;
			pEmfHeader->szlDevice.cy = rEmfHeader.szlDevice.cy;
			pEmfHeader->szlMillimeters.cx = rEmfHeader.szlMillimeters.cx;
			pEmfHeader->szlMillimeters.cy = rEmfHeader.szlMillimeters.cy;
			CopyRect((LPRECT)&pEmfHeader->rclFrame, (LPRECT)&rEmfHeader.rclFrame);
			//CopyRect((LPRECT)&pEmfHeader->rclBounds, (LPRECT)&rEmfHeader.rclBounds);
			
			DeleteEnhMetaFile(hemf);
			hemf = SetEnhMetaFileBits(uiSize, pData);
			delete [] pData;
			return TRUE;
		} // else stay with bogus emf
	}

	return FALSE;

}

///
/// Call this when finished with a created metafile DC
///
void SCEMFDCDispose(HDC& hEMFDC)
{
	if (!hEMFDC)
		return;

	//Close and delete it
	HENHMETAFILE	hEmfTemp = SCCloseEMF(hEMFDC);	
	::DeleteEnhMetaFile(hEmfTemp);
}

void SCInitEMFDCForEnumeration1(HDC hEMFDC, ENHMETAHEADER &hEmfHeader)
{
	if (!hEMFDC)
		return;

	::SetMapMode(hEMFDC,MM_ANISOTROPIC);
	::SetWindowOrgEx(hEMFDC,0,0,NULL);
	::SetViewportOrgEx(hEMFDC,0,0,NULL);
	::SetWindowExtEx(hEMFDC,(int)hEmfHeader.szlMillimeters.cx * 100,(int)hEmfHeader.szlMillimeters.cy * 100,NULL);
	::SetViewportExtEx(hEMFDC,(int)hEmfHeader.szlDevice.cx, (int)hEmfHeader.szlDevice.cy,NULL);
}

///
/// Call this to viewport and window before playing a metafile
///
void SCInitEMFDCForEnumeration(HDC hEMFDC, ENHMETAHEADER &EmfHeader, HDC hDCRef)
{
	if (!hEMFDC)
		return;

	::SetMapMode(hEMFDC,MM_ANISOTROPIC);

	// Set the window extent to rclFrame in pixels
	int width, height;
	// Get the characteristics of the output device.
	int iDpiX = GetDeviceCaps(hDCRef,  LOGPIXELSX);
	int iDpiY = GetDeviceCaps(hDCRef,  LOGPIXELSY);
	RECTL& rRect = EmfHeader.rclFrame;
	width = MulDiv(rRect.right - rRect.left + 1, iDpiX, L001MMPERINCH);
	height = MulDiv(rRect.bottom - rRect.top + 1, iDpiY, L001MMPERINCH);

	SetWindowExtEx(hEMFDC, width, height, NULL);

    // Set the viewport extent to reflect
    // rclFrame in target device units
	    // Get the physical characteristics of the reference DC
    float PixelsX = (float)GetDeviceCaps( hDCRef, HORZRES );
    float PixelsY = (float)GetDeviceCaps( hDCRef, VERTRES );
    float MMX = (float)GetDeviceCaps( hDCRef, HORZSIZE );
    float MMY = (float)GetDeviceCaps( hDCRef, VERTSIZE );
	DWORD dwInchesX = (DWORD)(float(EmfHeader.rclFrame.right*PixelsX)/(100.0f*MMX));
	DWORD dwInchesY = (DWORD)(float(EmfHeader.rclFrame.bottom*PixelsY)/(100.0f*MMY));

    SetViewportExtEx( hEMFDC, dwInchesX, dwInchesY, NULL);
}


static	BOOL	s_bEMRSMALLTEXTOUT = FALSE;
int CALLBACK SCEnhMetafileFilterProc(HDC hDC, HANDLETABLE FAR *lpHTable, ENHMETARECORD FAR *lpEMFR,  int nObj, LPARAM lpData )
{
	if (lpEMFR->iType == 108)
		s_bEMRSMALLTEXTOUT = TRUE;
	else
    // Do not play Escape
	if (lpEMFR->iType == 106)//EMR_EXTESCAPE)
		return 1;
	
	PlayEnhMetaFileRecord(hDC, lpHTable, lpEMFR, nObj);
	return 1; 
}

HDC MSCreateEnhMetaFileFlt( LPTSTR szFileName, // Metafile filename 

                         float dwInchesX,    // Width in inches
                         float dwInchesY,    // Height in inches
                         float dwDPI )       // DPI (logical units)
{
    RECT   Rect = { 0, 0, 0, 0 };
    TCHAR   szDesc[] = _T("EMFExplorer\0Filtered EMF\0\0");
    HDC      hMetaDC, hScreenDC;
    float   PixelsX, PixelsY, MMX, MMY;

    // dwInchesX x dwInchesY in .01mm units
    SetRect(&Rect, 0, 0, (int)(dwInchesX*2540), (int)(dwInchesY*2540));

    // Get a Reference DC
    hScreenDC = GetDC( NULL );

    // Get the physical characteristics of the reference DC
    PixelsX = (float)GetDeviceCaps( hScreenDC, HORZRES );
    PixelsY = (float)GetDeviceCaps( hScreenDC, VERTRES );
    MMX = (float)GetDeviceCaps( hScreenDC, HORZSIZE );
    MMY = (float)GetDeviceCaps( hScreenDC, VERTSIZE );

    // Create the Metafile
    hMetaDC = CreateEnhMetaFile(hScreenDC, szFileName, &Rect, szDesc);
    // Release the reference DC
    ReleaseDC( NULL, hScreenDC );
    // Did you get a good metafile?
    if( hMetaDC == NULL )
        return NULL;

    // Anisotropic mapping mode
    SetMapMode( hMetaDC, MM_ANISOTROPIC );
    // Set the Windows extent
    SetWindowExtEx( hMetaDC, (int)(dwInchesX*dwDPI), (int)(dwInchesY*dwDPI), NULL );

    // Set the viewport extent to reflect
    // dwInchesX" x dwInchesY" in device units
    SetViewportExtEx( hMetaDC,
                      (int)((float)dwInchesX*25.4f*PixelsX/MMX),
                      (int)((float)dwInchesY*25.4f*PixelsY/MMY),
                      NULL );
    return hMetaDC;

}

///
/// Filter an EMF to remove undocumented records
///
BOOL SCPreFilterEMF(HENHMETAFILE& hEmf)
{
	ASSERT(hEmf);
	ENHMETAHEADER EmfHeader;
	if (!::GetEnhMetaFileHeader(hEmf, sizeof(ENHMETAHEADER), &EmfHeader))
	{
		DWORD dwErrCode = ::GetLastError();
		return FALSE;
	}
	HDC hDCRef = GetDC(NULL);

	CSize sizeEMF;
	SCGetEMFPlaySize(hEmf, sizeEMF);

	long lEmfDPIX = MulDiv(EmfHeader.szlDevice.cx, 254, EmfHeader.szlMillimeters.cx*10L);
	long lEmfDPIY = MulDiv(EmfHeader.szlDevice.cy, 254, EmfHeader.szlMillimeters.cy*10L);
	int iDpiX = GetDeviceCaps(hDCRef,  LOGPIXELSX);
	int iDpiY = GetDeviceCaps(hDCRef,  LOGPIXELSY);

	CRect rcPlay((LPRECT)&EmfHeader.rclFrame);
	HDC hNewEMFDC = CreateEnhMetaFile(hDCRef, (LPCTSTR)NULL, (LPRECT)&EmfHeader.rclFrame, NULL);
	SCInitEMFDCForEnumeration1(hNewEMFDC, EmfHeader);

	if (EnumEnhMetaFile(hNewEMFDC, hEmf,  (ENHMFENUMPROC)SCEnhMetafileFilterProc, (LPVOID)NULL, (LPRECT)&rcPlay))
	{
		if (s_bEMRSMALLTEXTOUT)
		{
			DeleteEnhMetaFile(hEmf);
			hEmf = SCCloseEMF(hNewEMFDC);
		}
	}

	SCEMFDCDispose(hNewEMFDC);
	ReleaseDC(NULL, hDCRef);
	return TRUE;
}

#if 0
// Zzzz!... Let them sleep.
#define MS_LOGX GetDeviceCaps(hDC, LOGPIXELSX)
#define MS_LOGY GetDeviceCaps(hDC, LOGPIXELSY)
#define MS_HIMETRICINCH 2540

void FAR PASCAL MSHiMetrictoDP(HDC hDC, POINT &pt)

{ 
   pt.x = MulDiv(pt.x, MS_LOGX, MS_HIMETRICINCH);

   // The minus sign is required because the Y axis
   // points down in the MM_TEXT mapping mode
   pt.y = -MulDiv(pt.y, MS_LOGY, MS_HIMETRICINCH);

} 

void FAR PASCAL MSDPtoHiMetric(HDC hDC, POINT &pt)

{ 
   pt.x = MulDiv(pt.x, MS_HIMETRICINCH, MS_LOGX);

   // The minus sign is required because the Y axis
   // points down in the MM_TEXT mapping mode
   pt.y = -MulDiv(pt.y, MS_HIMETRICINCH, MS_LOGY);

} 
#endif